/*
 * Copyright (C) 2011 JiangHongTiao <jjurco.sk_gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package sk.lieskove301.jianghongtiao.liaad.prisoner.ga.selection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import sk.lieskove301.jianghongtiao.liaad.prisoner.agent.Agent;
import sk.lieskove301.jianghongtiao.liaad.prisoner.agent.AgentDescComparator;
import sk.lieskove301.jianghongtiao.liaad.prisoner.agent.AgentIncComparator;
import sk.lieskove301.jianghongtiao.liaad.prisoner.gene.GeneticInfo;
import sk.lieskove301.jianghongtiao.liaad.prisoner.memory.ForgettingEnum;
import sk.lieskove301.jianghongtiao.liaad.prisoner.payoff.PayoffValue;
import sk.lieskove301.jianghongtiao.liaad.prisoner.strategy.FightResultEnum;

/**
 * inspired by pseudocode published in notes.
 * <a href="www.cs.ucc.ie/~dgb/courses/tai/notes/handout12.pdf">
 * www.cs.ucc.ie/~dgb/courses/tai/notes/handout12.pdf</a>
 * 
 * Date of create: May 24, 2011
 *
 * @author JiangHongTiao <jjurco.sk_gmail.com>
 * @version 2011.0524
 */
public class RouletteWheelSelection implements Selection{
    
    private double approxSelectionSize = 0;

    /**
     * Individuals are given a probability of being selected that is directly 
     * proportionate to their fitness. Two individuals are then chosen randomly 
     * based on these probabilities and produce offspring. 
     * @param population
     * @param number
     * @return 
     */
    private List<Agent> selectAgents(List<Agent> population, int number, int round) {
        double fitnessSum = 0;
        Collections.sort(population, new AgentDescComparator());
        double incNum = population.get(population.size()-1).getScore();
        List<Agent> result = new ArrayList<Agent>();
        //compute sum of all scores of agents
        for (Iterator<Agent> it = population.iterator(); it.hasNext();) {
            Agent agent = it.next();
            fitnessSum += agent.getScore()+incNum;
        }
        Random rand = new Random();
        double randNumber = rand.nextDouble();
        double sum = 0;
        for (Iterator<Agent> it = population.iterator(); it.hasNext();) {
            Agent agent = it.next();
            sum += (agent.getScore()+incNum) / fitnessSum;
            if(sum > 1){
                //System.err.println("Variable sum should not be greater than 1, but was: "+sum);
                //throw new IllegalStateException();
            }
            if(randNumber < sum){
                result.add(agent);
            }
            if(result.size() >= number){
                break;
            }
        }
        if(((result.size() < 2) || (result.size() == population.size())) && (round < 20)){
            return selectAgents(population, number, round + 1);
        }
        return result;
    }

    public List<Agent> selectAgents(List<Agent> population) {
        int selSize = (int)(population.size()*approxSelectionSize);
        return selectAgents(population, selSize, 0);
    }

    public void setAproxSelectionSize(double percent) {
        if((percent <= 0) || (percent >= 1)){
            throw new IllegalArgumentException("percent should be in range 0..1. 0 and 1 are excluded.");
        }
        this.approxSelectionSize = percent;
    }
}
